from __future__ import print_function ## a hack to enable print as a function. This MUST come as the first line.
import logging
import os
import sys
import puza_row as pr
import puza_stream_table as pst
import puza_memory_table as pmt
import puzautils.table.puza_filter as pf
import puzautils.table.puza_operator as po
import puzautils.table.puza_index as pindex

def table_compress(input_filename, output_filename, block_number):
    InputTable = pst.PuzaStreamTable()
    InputTable.InputFile(input_filename)
    OutputTable = pst.PuzaStreamTable()
    OutputTable.OutputFile(output_filename)
    while True:
        raw_obj = InputTable.StreamInRow()
        if raw_obj != None:
            r = pr.PuzaRow.Load(raw_obj)
            OutputTable.StreamOutRow(r, lformat = 1, block_number = block_number)
        else:
            break
    OutputTable.StreamClose()

def table_decompress(input_filename, output_filename):
    InputTable = pst.PuzaStreamTable()
    InputTable.InputFile(input_filename)
    OutputTable = pst.PuzaStreamTable()
    OutputTable.OutputFile(output_filename)
    while True:
        raw_obj = InputTable.StreamInRow()
        if raw_obj != None:
            r = pr.PuzaRow.Load(raw_obj)
            OutputTable.StreamOutRow(r, lformat = 0)
        else:
            break
    OutputTable.StreamClose()

def table_filter(input_filename, output_filename, field_name, operator, lformat, block_number):
    #=============================================
    operators = {}
    operators['count'] = 'PuzaCountOperator'
    operators['distinct'] = 'PuzaDistinctOperator'
    operators['plain'] = 'PuzaOperator'
    #=============================================
    InputTable = pst.PuzaStreamTable()
    InputTable.InputFile(input_filename)
    f = pf.PuzaTableFilterSaveToTable(output_filename = output_filename, lformat = lformat, block_number = block_number)
    op = getattr(po, operators[operator])()
    f.FilterByField(InputTable, field_name, op)
    return

def table_make_index(input_filename, index_filename, key_name, lformat, index_mode):
    if index_mode == 0:
        pindex.PuzaTableIndexer.MakeIndex(input_filename, index_filename, key_name, lformat)
    else:
        pindex.PuzaTableIndexer.MakeIndexStream(input_filename, index_filename, key_name, lformat)

def table_seek_by_index(input_filename, index_filename, key_name):
    index_table = pindex.PuzaTableIndexer.ReadIndex(index_filename)
    result = index_table.Locate(key_name, input_filename)
    for record in result:
        print(record)

def table_verify(filename, mode):
    if mode == 0:
        ## memory      
        logging.info('Input mode: memory')  
        if pmt.PuzaMemoryTable.Verify(filename):
            print('no error.')
            logging.info('The table has no errors.')
        else:
            print('Table has some error.')
            logging.warning('Table has some error.')
    elif mode == 1:
        logging.info('Input mode: stream')
        if pst.PuzaStreamTable.Verify(filename):
            print('no error.')
            logging.info('The table has no errors.')
        else:
            print('Table has some error.')
            logging.warning('Table has some error.')
    return

def table_length(filename, mode):
    if mode == 0:
        ## memory      
        logging.info('Input mode: memory')  
        A = pmt.PuzaMemoryTable()
        A.InputFile(filename)
        l = len(A)
        print('Table size:' + str(l))
        logging.info('Table size:' + str(l))
    elif mode == 1:
        logging.info('Input mode: stream')
        A = pst.PuzaStreamTable()
        A.InputFile(filename)
        l = len(A)
        print('Table size:' + str(l))
        logging.info('Table size:' + str(l))
    return

def reconstruct_structure(raw_structure):
    structure_tree = {}
    for item_key, item_struct in raw_structure.iteritems():
        s = item_key.split('->')
        holder = structure_tree
        l = -1
        for name in s:            
            if not name in holder:
                holder[name] = {}
                holder[name]['children'] = {}
                holder[name]['level'] = l    
            l = l + 1
            if l == len(s) -1:
                holder[name]['counts'] = item_struct['counts']
                holder[name]['type'] = item_struct['type']
            holder = holder[name]['children']
    return structure_tree

def recurrent_print_structure(current_node, parent_node):
    childrens = current_node['children']
    if parent_node != 'root':
        level = current_node['level']
        s = ' '
        for i in range(level):
            s = s + '-'
        s = s + '' + parent_node + '\tLevel:' + str(level) +'\tCounts:' + str(current_node['counts']) +'\tType:' + str(current_node['type'])
        print(s)
        logging.info(s)
    if len(childrens) > 0:
        for child_name, child_node in childrens.iteritems():           
            recurrent_print_structure(child_node, child_name)
    return

def print_structure(structure):
    structure_tree = reconstruct_structure(structure)
    recurrent_print_structure(structure_tree['root'], 'root')

def table_structure(filename, mode):
    if mode == 0:
        ## memory
        logging.info('Input mode: memory')
        A = pmt.PuzaMemoryTable()
        A.InputFile(filename)        
        print_structure(A.GetStructure())         
    elif mode == 1:
        logging.info('Input mode: stream')
        A = pst.PuzaStreamTable()
        A.InputFile(filename)
        print_structure(A.GetStructure())        

def table_show_rows(filename, mode, start_n, end_n):
    row_buffers = []
    if mode == 0:
        ## memory
        logging.info('Input mode: memory')
        A = pmt.PuzaMemoryTable()
        A.InputFile(filename)
        row_buffers = A.Rows(start_n, end_n)
    elif mode == 1:
        logging.info('Input mode: stream')
        A = pst.PuzaStreamTable()
        A.InputFile(filename)
        row_buffers = A.Rows(start_n, end_n)
    for row in row_buffers:
        print(row)

def table_show(filename):
    A = pst.PuzaStreamTable()
    A.InputFile(filename)
    A.ReadLines(lambda x:print(x))   

def table_merge(output_filename, mode, block_number, tables):
    A = pst.PuzaStreamTable()
    A.OutputFile(output_filename)
    files = tables.split(',')
    for current_file in files:
        if not os.path.exists(current_file):
            print(files+' does not exists.')
        else:        
            ## open the first file
            B = pst.PuzaStreamTable
            B.InputFile(current_file)
            while True:
                obj = B.StreamInRow()
                if obj != None:
                    r = pr.PuzaRow.Load(obj)
                    A.StreamOutRow(r, lformat = mode, block_number = block_number)
                else:
                    break
    A.StreamClose()      